Powershell scripts/MalwareScanScript/MalwareScanScript.ps1 (223 lines of code) (raw):
<#
.DESCRIPTION
Runbook to initiate and monitor malware scans on Azure Storage Accounts with a specific tag using Interactive Azure login.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$SubscriptionId,
[Parameter(Mandatory = $true)]
[string]$TagKey = "ScanForMalware",
[Parameter(Mandatory = $true)]
[string]$TagValue = "True",
[Parameter(Mandatory = $true)]
[string]$OutputCsvPath
)
function Write-ErrorAndExit {
param([string]$ErrorMessage)
Write-Error $ErrorMessage
throw $ErrorMessage
}
function Connect-ToAzure {
param(
[string]$SubscriptionId
)
try {
Write-Output "Logging in to Azure using Interactive login..."
# This will prompt for login via a browser
Connect-AzAccount -ErrorAction Stop
# Set the Azure context to the specified Subscription
Set-AzContext -SubscriptionId $SubscriptionId -ErrorAction Stop
Write-Output "Azure context set to Subscription ID: $SubscriptionId"
}
catch {
Write-ErrorAndExit "Failed to set Azure context. Error: $_"
}
}
function Start-MalwareScan {
param(
[Microsoft.Azure.Commands.Management.Storage.Models.PSStorageAccount]$StorageAccount,
[string]$SubscriptionId
)
$resourceGroupName = $StorageAccount.ResourceGroupName
$storageAccountName = $StorageAccount.StorageAccountName
$uri = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Storage/storageAccounts/$storageAccountName/providers/Microsoft.Security/defenderForStorageSettings/current/startMalwareScan?api-version=2024-10-01-preview"
try {
$response = Invoke-AzRestMethod -Method POST -Uri $uri -ErrorAction Stop
return $response
}
catch {
Write-Warning "Failed to start scan for '$storageAccountName'. Error: $_"
return $null
}
}
function Get-MalwareScanStatus {
param(
[Microsoft.Azure.Commands.Management.Storage.Models.PSStorageAccount]$StorageAccount,
[string]$SubscriptionId
)
$resourceGroupName = $StorageAccount.ResourceGroupName
$storageAccountName = $StorageAccount.StorageAccountName
$uri = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Storage/storageAccounts/$storageAccountName/providers/Microsoft.Security/defenderForStorageSettings/current/malwareScans/latest?api-version=2024-10-01-preview"
try {
$response = Invoke-AzRestMethod -Method GET -Uri $uri -ErrorAction Stop
return $response
}
catch {
Write-Warning "Failed to get scan status for '$storageAccountName'. Error: $_"
return $null
}
}
try {
# Authenticate and set the subscription context
Connect-ToAzure -SubscriptionId $SubscriptionId
Write-Output "`nFetching storage accounts with tag '$TagKey=$TagValue'..."
$storageAccounts = Get-AzStorageAccount | Where-Object { $_.Tags[$TagKey] -eq $TagValue }
if (-not $storageAccounts) {
Write-ErrorAndExit "No storage accounts found with tag '$TagKey=$TagValue'."
}
Write-Output "Found $($storageAccounts.Count) storage account(s) with the specified tag.`n"
$scanStatuses = @{}
$scanResults = @()
foreach ($storageAccount in $storageAccounts) {
Write-Output "Starting malware scan for storage account: $($storageAccount.StorageAccountName)"
$response = Start-MalwareScan -StorageAccount $storageAccount -SubscriptionId $SubscriptionId
if ($response) {
$responseContent = $response.Content | ConvertFrom-Json
if ($responseContent.scanStatus -in @("Queued", "WaitingForCompletion")) {
Write-Output "Successfully initiated scan for '$($storageAccount.StorageAccountName)'. Status: $($responseContent.scanStatus)`n"
$scanStatuses[$storageAccount.StorageAccountName] = @{
Status = $responseContent.scanStatus
LastChecked = Get-Date
ScanDetails = $responseContent
}
}
else {
Write-Warning "Failed to start scan for '$($storageAccount.StorageAccountName)'. Status: $($responseContent.scanStatus)`n"
$scanStatuses[$storageAccount.StorageAccountName] = @{
Status = "Failed"
LastChecked = Get-Date
ScanDetails = $responseContent
}
}
}
else {
Write-Warning "Failed to start scan for '$($storageAccount.StorageAccountName)'.`n"
$scanStatuses[$storageAccount.StorageAccountName] = @{
Status = "Failed"
LastChecked = Get-Date
ScanDetails = $null
}
}
}
# Continuously check the status of each scan until all are completed or failed
$allCompleted = $false
while (-not $allCompleted) {
$allCompleted = $true
foreach ($accountName in $scanStatuses.Keys) {
$currentStatus = $scanStatuses[$accountName]
if ($currentStatus.Status -notin @("Completed", "Failed")) {
$allCompleted = $false
$storageAccount = $storageAccounts | Where-Object { $_.StorageAccountName -eq $accountName }
$status = Get-MalwareScanStatus -StorageAccount $storageAccount -SubscriptionId $SubscriptionId
if ($status) {
$statusContent = $status.Content | ConvertFrom-Json
$scanStatuses[$accountName].Status = $statusContent.scanStatus
$scanStatuses[$accountName].LastChecked = Get-Date
$scanStatuses[$accountName].ScanDetails = $statusContent
$blobSummary = $statusContent.scanSummary.blobs
Write-Output "Storage Account: $accountName"
Write-Output " Status: $($statusContent.scanStatus)"
Write-Output " Total Blobs Scanned: $($blobSummary.totalBlobsScanned)"
Write-Output " Malicious Blobs Count: $($blobSummary.maliciousBlobsCount)"
Write-Output " Scanned Blobs in GB: $([math]::Round($blobSummary.scannedBlobsInGB, 4))`n"
}
else {
Write-Warning "Failed to get scan status for '$accountName'.`n"
}
}
}
if (-not $allCompleted) {
Write-Output "Waiting 10 seconds before next status check...`n"
Start-Sleep -Seconds 10
}
}
# Collect scan results for CSV export
$overallSummary = @{
TotalStorageAccounts = $storageAccounts.Count
SuccessfulScans = 0
FailedScans = 0
TotalBlobsScanned = 0
TotalMaliciousBlobs = 0
TotalSkippedBlobs = 0
TotalScannedBlobsInGB = 0.0
EstimatedTotalScanCost = 0.0
}
foreach ($accountName in $scanStatuses.Keys) {
$status = $scanStatuses[$accountName]
Write-Output "----------------------------------------"
Write-Output "Storage Account: $accountName"
Write-Output "Status: $($status.Status)"
Write-Output "Last Checked: $($status.LastChecked)"
if ($status.Status -eq "Completed" -and $status.ScanDetails) {
$details = $status.ScanDetails
Write-Output " Scan ID: $($details.scanId)"
Write-Output " Scan Start Time: $($details.scanStartTime)"
Write-Output " Scan End Time: $($details.scanEndTime)"
Write-Output " Total Blobs Scanned: $($details.scanSummary.blobs.totalBlobsScanned)"
Write-Output " Malicious Blobs Count: $($details.scanSummary.blobs.maliciousBlobsCount)"
Write-Output " Skipped Blobs Count: $($details.scanSummary.blobs.skippedBlobsCount)"
Write-Output " Scanned Blobs in GB: $($details.scanSummary.blobs.scannedBlobsInGB)"
Write-Output " Estimated Scan Cost (USD): $($details.scanSummary.estimatedScanCostUSD)`n"
# Update overall summary
$overallSummary.SuccessfulScans++
$overallSummary.TotalBlobsScanned += $details.scanSummary.blobs.totalBlobsScanned
$overallSummary.TotalMaliciousBlobs += $details.scanSummary.blobs.maliciousBlobsCount
$overallSummary.TotalSkippedBlobs += $details.scanSummary.blobs.skippedBlobsCount
$overallSummary.TotalScannedBlobsInGB += $details.scanSummary.blobs.scannedBlobsInGB
$overallSummary.EstimatedTotalScanCost += $details.scanSummary.estimatedScanCostUSD
# Add to the CSV results
$scanResults += [PSCustomObject]@{
StorageAccountName = $accountName
Status = $status.Status
LastChecked = $status.LastChecked
ScanId = $details.scanId
ScanStartTime = $details.scanStartTime
ScanEndTime = $details.scanEndTime
TotalBlobsScanned = $details.scanSummary.blobs.totalBlobsScanned
MaliciousBlobsCount = $details.scanSummary.blobs.maliciousBlobsCount
SkippedBlobsCount = $details.scanSummary.blobs.skippedBlobsCount
ScannedBlobsInGB = $details.scanSummary.blobs.scannedBlobsInGB
EstimatedScanCostUSD = $details.scanSummary.estimatedScanCostUSD
}
}
elseif ($status.Status -eq "Failed") {
Write-Output " Scan not started or failed.`n"
$overallSummary.FailedScans++
}
else {
Write-Output " Scan Status: $($status.Status)`n"
}
}
# Export results to CSV
Write-Output "Exporting scan results to CSV..."
$scanResults | Export-Csv -Path $OutputCsvPath -NoTypeInformation
Write-Output "Results exported to $OutputCsvPath"
# Display the overall summary
Write-Output "----------------------------------------"
Write-Output "`nOverall Summary:"
Write-Output " Total Storage Accounts: $($overallSummary.TotalStorageAccounts)"
Write-Output " Successful Scans: $($overallSummary.SuccessfulScans)"
Write-Output " Failed Scans: $($overallSummary.FailedScans)"
Write-Output " Total Blobs Scanned: $($overallSummary.TotalBlobsScanned)"
Write-Output " Total Malicious Blobs: $($overallSummary.TotalMaliciousBlobs)"
Write-Output " Total Skipped Blobs: $($overallSummary.TotalSkippedBlobs)"
Write-Output " Total Scanned Blobs in GB: $([math]::Round($overallSummary.TotalScannedBlobsInGB, 4))"
Write-Output " Estimated Total Scan Cost (USD): $([math]::Round($overallSummary.EstimatedTotalScanCost, 6))"
}
catch {
Write-ErrorAndExit "An error occurred: $_"
}
finally {
Write-Output "`nScript execution completed."
}